﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace CPPEI.Net.Original.UDP
{
    public class UDPClient : IDisposable
    {
        private int _port;
        /// <summary>
        /// 用于通讯的套接字
        /// </summary>
        private Socket _socket;

        private EndPoint _remoteEndPoint;

        private IPackageHandlerFactory _responseHandlerFactory;

        byte[] _receiveBuffer;
        private PackagePool _netBufferPool;
        private bool _isReceiving = false;

        private UDPClientReceiveThread _receiveThread;

        public UDPClient()
        {
            _socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
        }

        public UDPClient(int port, IPackageHandlerFactory responseHandlerFactory)
        {
            _port = port;
            _responseHandlerFactory = responseHandlerFactory;
            // 8k
            _receiveBuffer = new byte[8 * 1024];
            _netBufferPool = new PackagePool();

            _socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
        }

        /// <summary>
        /// 
        /// </summary>
        public void StartReceive(string ip)
        {
            IPEndPoint ipep = new IPEndPoint(IPAddress.Parse(ip), _port);
            _socket.Bind(ipep);
            _receiveThread = new UDPClientReceiveThread(this);
            _receiveThread.Start();
        }

        public void StopReceive()
        {
            _receiveThread.Stop();
        }

        public void SendTo(string ip, int port, Package package)
        {
            IPAddress address = IPAddress.Parse(ip);
            IPEndPoint endPoint = new IPEndPoint(address, port);
            try
            {
                int len = _socket.SendTo(package.Buffer, endPoint);
                while (len < package.Buffer.Length)
                {
                    System.Diagnostics.Debug.WriteLineIf(len < package.Buffer.Length, "发送的数据长度小于实际的数据长度,继续发送");
                    len += _socket.SendTo(package.Buffer, len, package.Buffer.Length - len, SocketFlags.None, endPoint);
                }

                //LatestActiveTicks = DateTime.Now.Ticks;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(string.Format("发送数据出错,ex:{0}", ex));
            }
        }

        public void SendTo(string ip, Package package)
        {
            IPAddress address = IPAddress.Parse(ip);
            IPEndPoint endPoint = new IPEndPoint(address, _port);
            try
            {
                int len = _socket.SendTo(package.Buffer, endPoint);
                while (len < package.Buffer.Length)
                {
                    System.Diagnostics.Debug.WriteLineIf(len < package.Buffer.Length, "发送的数据长度小于实际的数据长度,继续发送");
                    len += _socket.SendTo(package.Buffer, len, package.Buffer.Length - len, SocketFlags.None, endPoint);
                }

                //LatestActiveTicks = DateTime.Now.Ticks;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(string.Format("发送数据出错,ex:{0}", ex));
            }
        }

        public void ReceiveAsync()
        {
            try
            {
                if (!_isReceiving)
                {
                    _isReceiving = true;
                    _socket.BeginReceive(_receiveBuffer,
                        0,
                        _receiveBuffer.Length,
                        SocketFlags.None,
                        new AsyncCallback(OnReceiveCallback),
                        null);
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(string.Format("接收数据出错,ex:{0}", ex));
            }
        }

        private void OnReceiveCallback(IAsyncResult ar)
        {
            try
            {
                int receiveLen = _socket.EndReceive(ar);
                if (receiveLen > 0)
                {
                    _netBufferPool.AppendBuffer(_receiveBuffer, receiveLen);

                    // 取得一个数据包
                    Package package = _netBufferPool.GetNetBuffer();
                    if (package != null)
                    {
                        IPackageHandler handler = _responseHandlerFactory.Create(package.Command);
                        handler.Handle(package);
                    }
                }
            }
            catch (Exception ex)
            {
            }
            _isReceiving = false;
        }

        /// <summary>
        /// 接收数据
        /// </summary>
        public Package Receive()
        {
            try
            {
                int receiveLen = _socket.Receive(_receiveBuffer, 0, _receiveBuffer.Length, SocketFlags.None);
                if (receiveLen > 0)
                {
                    _netBufferPool.AppendBuffer(_receiveBuffer, receiveLen);

                    Package netBuffer = _netBufferPool.GetNetBuffer();
                    return netBuffer;
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(string.Format("接收数据出错,ex:{0}", ex));
            }
            return null;
        }

        public void Dispose()
        {
            StopReceive();
            if (_socket != null)
            {
                _socket.Close();
            }
        }
    }
}
